package org.xacmlauth.service.endpoint;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.xacmlauth.core.context.RequestType;
import org.xacmlauth.core.context.ResponseType;
import org.xacmlauth.core.domain.DeletePolicyRequest;
import org.xacmlauth.core.domain.DeletePolicyResponse;
import org.xacmlauth.core.domain.EnforceInfo;
import org.xacmlauth.core.domain.EnforceRequest;
import org.xacmlauth.core.domain.EnforceRespInfo;
import org.xacmlauth.core.domain.EnforceResponse;
import org.xacmlauth.core.domain.ListPolicyRequest;
import org.xacmlauth.core.domain.ListPolicyResponse;
import org.xacmlauth.core.domain.ObjectFactory;
import org.xacmlauth.core.domain.PolicyInfo;
import org.xacmlauth.core.domain.RegisterPolicyRequest;
import org.xacmlauth.core.domain.RegisterPolicyResponse;
import org.xacmlauth.core.policy.PolicySetType;
import org.xacmlauth.core.policy.PolicyType;
import org.xacmlauth.dto.PolicyDTO;
import org.xacmlauth.service.AuthService;
import org.xacmlauth.util.ContextManager;


@Endpoint
public class AuthServiceEndPoint {
	
	private AuthService authService;
	private ObjectFactory objectFactory = new ObjectFactory();
	private static final String SERVICE_NS = "urn:org:xamlauth:domain";
	private static final Log LOG = LogFactory.getLog(AuthServiceEndPoint.class);
	
	@Autowired
	public AuthServiceEndPoint(AuthService authService){
		this.authService = authService;
	}
	
	@PayloadRoot(namespace = SERVICE_NS, localPart = "registerPolicyRequest")
	public RegisterPolicyResponse registerPolicy(RegisterPolicyRequest request) throws Exception {
		PolicyInfo policyInfo = request.getPolicyInfo();
		String clientID = policyInfo.getClientID();
		PolicyType policy = policyInfo.getPolicy();
		PolicySetType policySet = policyInfo.getPolicySet();
		
		LOG.debug("Register policy recieved "+clientID);
		
		if(policy != null && policySet != null){
			throw new Exception("Only Policy or PolicySet can have value");
		}else if (policy == null && policySet == null){
			throw new Exception("At least one value must be set to Policy or PolicySet");
		}
		
		if(clientID == null || clientID.isEmpty()){
			throw new Exception("Client ID can not be null or empty");
		}
		
		JAXBContext context = ContextManager.getPolicyContext();
		Marshaller marshaller = context.createMarshaller();
		
		StringWriter content = new StringWriter();
		org.xacmlauth.core.policy.ObjectFactory policyObjectFactory = new org.xacmlauth.core.policy.ObjectFactory();
		
		JAXBElement jaxbElement = null;
		if(policy != null){
			jaxbElement = policyObjectFactory.createPolicy(policy);
		}else if(policySet != null){
			jaxbElement = policyObjectFactory.createPolicySet(policySet);
		}
		marshaller.marshal(jaxbElement, content);
		
		List<PolicyDTO> policyList = authService.listPolicies(clientID);
		
		if(policyList!= null && policyList.size() > 0){
			LOG.info("Found more policies already registered for "+clientID);
			authService.deletePolicy(clientID);
		}
		
		PolicyDTO policyDTO = new PolicyDTO();
		policyDTO.setContent(content.toString());
		policyDTO.setClientId(clientID);
		policyDTO.setDescription(policyInfo.getDescription());
		policyDTO.setName(policyInfo.getName());
		
		boolean status = authService.registerPolicy(policyDTO);
		
		RegisterPolicyResponse response = objectFactory.createRegisterPolicyResponse();
		response.setStatus(status);
		return response;
	}
	
	@PayloadRoot(namespace = SERVICE_NS, localPart = "enforceRequest")
	public EnforceResponse enforce(EnforceRequest request) throws Exception {
		EnforceInfo enforceInfo = request.getEnforceInfo();
		String clientID = enforceInfo.getClientID();
		RequestType enforceRequest = enforceInfo.getRequest();
		
		LOG.debug("Enforcement request recieved "+clientID);
		
		if(clientID == null || clientID.isEmpty()){
			throw new Exception("Client ID can not be null or empty");
		}
		
		if(enforceRequest == null){
			throw new Exception("Enforce request can not null");
		}
		
		JAXBContext context = ContextManager.getRequestContext();
		Marshaller marshaller = context.createMarshaller();
		
		StringWriter content = new StringWriter();
		org.xacmlauth.core.context.ObjectFactory contextObjectFactory = new org.xacmlauth.core.context.ObjectFactory();
		JAXBElement<RequestType> jaxbContext = contextObjectFactory.createRequest(enforceRequest);
		marshaller.marshal(jaxbContext, content);

		String enforceResponse = authService.enforce(clientID, content.toString());
		EnforceResponse response = objectFactory.createEnforceResponse();
		
		EnforceRespInfo enforceRespInfo = new EnforceRespInfo();
		enforceRespInfo.setClientID(clientID);
		
		Unmarshaller unmarshaller = context.createUnmarshaller();
		JAXBElement<?> element = (JAXBElement<?>)unmarshaller.unmarshal(new StringReader(enforceResponse));
		ResponseType r = (ResponseType)element.getValue();
		enforceRespInfo.setResponse(r);
		
		response.setRespInfo(enforceRespInfo);
		return response;
	}
	
	@PayloadRoot(namespace = SERVICE_NS, localPart = "listPolicyRequest")
	public ListPolicyResponse listPolicy(ListPolicyRequest request) throws Exception {
		String clientID = request.getClientID();
		LOG.debug("Policy list request recieved "+clientID);
		
		List<PolicyDTO> policyList = authService.listPolicies(clientID);
		ListPolicyResponse response = objectFactory.createListPolicyResponse();

		if(policyList != null && policyList.size() > 0){
			PolicyDTO policy = policyList.get(0);
			
			if(policy != null){
				PolicyInfo policyInfo = new PolicyInfo();
				policyInfo.setClientID(policy.getClientId());
				policyInfo.setName(policy.getName());
				policyInfo.setDescription(policy.getDescription());
				
				response.setPolicyInfo(policyInfo);
			}else {
				LOG.warn("No policies found for "+clientID);
			}
		}
		
		
		return response;
	}
	
	@PayloadRoot(namespace = SERVICE_NS, localPart = "deletePolicyRequest")
	public DeletePolicyResponse deletePolicy(DeletePolicyRequest request) throws Exception {
		String clientID = request.getClientID();
		LOG.debug("Policy delete request recieved "+clientID);
		
		boolean status = authService.deletePolicy(clientID);
		DeletePolicyResponse response = objectFactory.createDeletePolicyResponse();
		response.setStatus(status);
		
		return response;
	}

}
